﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Xml.Serialization;
using System.IO;
using BbzaApi.Core;

namespace BbzaApi
{
    /// <summary>
    /// This service allows the interaction with the broadbandstats.co.za API.
    /// </summary>
    /// <remarks>
    /// See - http://api.broadbandstats.co.za/docs/
    /// </remarks>
    public class BbzaService
    {
        protected string m_apiUsername;
        protected string m_apiPassword;
        protected string m_apiUrl;
        protected string m_version = null;

        /// <summary>
        /// Initializes a new instance of the BbzaApi.BbzaService class
        /// using the default API version.
        /// </summary>
        /// <param name="apiUsername_">API Key.</param>
        /// <param name="apiPassword_">Secret API Key.</param>
        /// <param name="apiUrl_">URL of API</param>
        public BbzaService(string apiUsername_, string apiPassword_, string apiUrl_)
            : this(apiUsername_, apiPassword_, apiUrl_, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of the BbzaApi.BbzaService class.
        /// using the specified API version.
        /// </summary>
        /// <param name="apiUsername_">API Key.</param>
        /// <param name="apiPassword_">Secret API Key.</param>
        /// <param name="apiUrl_">URL of API</param>
        /// <param name="version_">Version of API to use</param>
        public BbzaService(string apiUsername_, string apiPassword_, string apiUrl_, string version_)
        {
            m_apiUsername = apiUsername_;
            m_apiPassword = apiPassword_;
            m_apiUrl = apiUrl_;
            m_version = version_;
        }

        /// <summary>
        /// Retrieve a list of exchanges and availability via a phone number lookup
        /// </summary>
        /// <param name="phoneNumber_">The phone number to lookup.</param>
        /// <returns>The matching exchanges</returns>
        public Exchanges GetExchangeByPhone(string phoneNumber_)
        {
            string url = m_apiUrl + "/availability/phone/" + phoneNumber_;

            Exchanges exchanges = Extract<Exchanges>(url);

            return exchanges;
        }

        /// <summary>
        /// Retrieve a list of exchanges and availability via a phone number lookup
        /// </summary>
        /// <param name="phoneNumbers_">The phone numbers to lookup.</param>
        /// <returns>The matching exchanges</returns>
        public Exchanges GetExchangeByPhone(string[] phoneNumbers_)
        {
            string numbers = String.Join(",", phoneNumbers_);
            string url = m_apiUrl + "/availability/phone/" + numbers;

            Exchanges exchanges = Extract<Exchanges>(url);

            return exchanges;
        }

        /// <summary>
        /// Retrieve a list of exchanges and availability via a exchange code lookup
        /// </summary>
        /// <param name="exchangeCode_">The phone number to lookup.</param>
        /// <returns>The matching exchanges</returns>
        public Exchanges GetExchangeByCode(string exchangeCode_)
        {
            string url = m_apiUrl + "/availability/exchange/" + exchangeCode_;

            Exchanges exchanges = Extract<Exchanges>(url);

            return exchanges;
        }

        /// <summary>
        /// Retrieve a list of exchanges and availability via a exchange code lookup
        /// </summary>
        /// <param name="exchangeCodes_">The exchange codes to lookup.</param>
        /// <returns>The matching exchanges</returns>
        public Exchanges GetExchangeByCode(string[] exchangeCodes_)
        {
            string codes = String.Join(",", exchangeCodes_);
            string url = m_apiUrl + "/availability/exchange/" + codes;

            Exchanges exchanges = Extract<Exchanges>(url);

            return exchanges;
        }

        /// <summary>
        /// Retrieve a list of exchanges and availability via searching
        /// through a bounded box.
        /// </summary>
        /// <param name="neLat">North East Longitude</param>
        /// <param name="neLat">North East Latitude</param>
        /// <param name="swLng">South West Longitude</param>
        /// <param name="swLat">South West Latitude</param>
        /// <returns>The matching exchanges</returns>
        public Exchanges GetExchangeByCoordinates(string neLng, string neLat, string swLng, string swLat)
        {
            string codes = String.Join(",", new string[] { neLng, neLat, swLng, swLat });
            string url = m_apiUrl + "/search/geo-range/" + codes;

            Exchanges exchanges = Extract<Exchanges>(url);

            return exchanges;
        }

        /// <summary>
        /// Generic method for extracting data.
        /// </summary>
        /// <typeparam name="TTarget">Target type to extract</typeparam>
        /// <param name="requestUrl_">URL to request data.</param>
        /// <returns>TTarget result of the extraction</returns>
        protected TTarget Extract<TTarget>(string requestUrl_) where TTarget : class, new()
        {
            string postData = GetPostData();
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

            var request = (HttpWebRequest)WebRequest.Create(requestUrl_);
            request.Credentials = new NetworkCredential(m_apiUsername, m_apiPassword);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = byteArray.Length;

            TTarget targetData = new TTarget();

            try
            {
                XmlSerializer serializer = new XmlSerializer(typeof(TTarget));
                Stream dataStream = request.GetRequestStream();
                dataStream.Write(byteArray, 0, byteArray.Length); //write post data
                dataStream.Close();

                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream());
                    string responseText = reader.ReadToEnd();

                    using (var stringReader = new StringReader(responseText))
                    {
                        targetData = (TTarget)serializer.Deserialize(stringReader);
                    }
                }
            }
            catch (WebException ex_)
            {
                //Add better error handling
                throw ex_;
            }

            return targetData;
        }

        protected string GetPostData()
        {
            string postData = "output_format=xml";
            if (m_version != null)
            {
                postData += string.Format("&version=", m_version);
            }
            return postData;
        }
    }
}